﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;

namespace T9Silverlight
{

    using T9ServiceReference1;
    

    public partial class MainPage : UserControl
    {
        private const string DIGIT_BUTTONS_STRING = "2 3 4 5 6 7 8 9";
        private const char NEXT_BUTTON = '0';
        private const char SPACE_BUTTON = '#';
        private const char QUIT_BUTTON = '1';
        private const char BACK_BUTTON = '*';
        private const int MAX_DISPLAY_LENGTH = 50;

        private static readonly Dictionary<char,string> _Labels =
            new Dictionary<char,string>
        {
            {QUIT_BUTTON, ""},
            {BACK_BUTTON, "<"},
            {NEXT_BUTTON, "~"},
            {SPACE_BUTTON, ""},
            {'2', "abc"},
            {'3', "def"},
            {'4', "ghi"},
            {'5', "jkl"},
            {'6', "mno"},
            {'7', "pqrs"},
            {'8', "tuv"},
            {'9', "wxyz"}
        };

        private static Dictionary<Boolean, String> _toggleLabels =
            new Dictionary<bool, string> { { true, "1 2 3" }, { false, "a b c" } };

        private string _frozenHistory = ""; //The text entered that can't be backspaced
        private string _currentInputs = ""; //The string of digits entered that make up the current word
        private string _currentWordDisplay = "";
        private bool _numericToggleOn = false;

        private Dictionary<string, List<String>> _wordPossibilities = new Dictionary<string, List<String>>();

        private static T9ServiceClient client = new T9ServiceClient();

        public MainPage()
        {
            InitializeComponent();
            SetUpButtonContents(LayoutRoot); //Set the correct labels on all the buttons

            //add the event handler to the word lookup call
            client.GetWordPossibilitiesCompleted +=
                new EventHandler<GetWordPossibilitiesCompletedEventArgs>(client_GetWordPossibilitiesCompleted);

            //initialize the web service by forcing it to use the engine
            client.GetWordPossibilitiesAsync("");
        }//end MainPage()

        /// <summary>
        /// Updates the text box display
        /// </summary>
        private void UpdateDisplay()
        {
            if (!_numericToggleOn)
            {
                //update the word to display if we can
                if (_wordPossibilities.ContainsKey(_currentInputs))
                    _currentWordDisplay = _wordPossibilities[_currentInputs].First();
                else if (_currentInputs.Length < 1) //check to see if we're here by mistake
                    return;
                else
                { //otherwise go fetch a word to display
                    client.GetWordPossibilitiesAsync(_currentInputs);
                    return;
                }
            }
            else
            {
                _currentWordDisplay = _currentInputs;
            }//end if else numericToggle

            //update the textbox
            textBox1.Text = (_frozenHistory + " " + _currentWordDisplay);

            //make sure it's not too long
            //var length = textBox.MaxLength;
            //textBox.Text = textBox.Text.Substring(Math.Max(0,length - MAX_DISPLAY_LENGTH));
        }//end UpdateDisplay

        /// <summary>
        /// Callback method that's launched when a GetWordPossibilities service call is completed.
        /// Updates the local wordPossibilities dictionary and calls UpdateDisplay
        /// </summary>
        private void client_GetWordPossibilitiesCompleted(object sender, T9ServiceReference1.GetWordPossibilitiesCompletedEventArgs e)
        {
            string digitString = e.Result.First();
            e.Result.RemoveAt(0);
            var words = e.Result.ToList<string>();
            _wordPossibilities[digitString] =  words;
            UpdateDisplay();
        }//end client_GetWordPossibilitiesCompleted

        /// <summary>
        /// Event handler for a T9 Button click
        /// Calls the T9Service if necessary
        /// </summary>
        private void T9Button_Click(object sender, RoutedEventArgs e)
        {
            Button senderBtn = (Button)sender;
            //check to see if we're in the numeric mode
            if (_numericToggleOn)
            {
                char btnDigit;
                //need to see what kind of button was pressed
                if (IsDigitButton(senderBtn, out btnDigit) || btnDigit == '1' || btnDigit == '0')
                {
                    //all we need to do for digit buttons in numeric mode is add the digit to the current word
                    _currentInputs += btnDigit.ToString();
                    UpdateDisplay();
                }
                else if (IsSpaceButton(senderBtn))
                {
                    //add the current word to the frozen history, then reset the current word
                    _frozenHistory += " " + _currentInputs;
                    _currentInputs = "";
                }
                else if (IsBackButton(senderBtn))
                {
                    //backspace if we can
                    if (_currentInputs.Length >= 1)
                    {
                        _currentInputs = _currentInputs.Substring(0, _currentInputs.Length - 1);
                        UpdateDisplay();
                    }
                }//end which button?

            }//end if numeric mode
            else //otherwise we're in T9 mode
            {
                char btnDigit;
                //need to see what kind of button was pressed
                if (IsDigitButton(senderBtn, out btnDigit))
                {
                    _currentInputs += btnDigit.ToString();
                    //check to see if we know about this digit string yet
                    if (_wordPossibilities.ContainsKey(_currentInputs))
                        UpdateDisplay();
                    else
                        //otherwise we need to ask the server for the new word Possibilities
                        client.GetWordPossibilitiesAsync(_currentInputs);
                        //once that async call completes, client_GetWordPossibilitiesCompleted
                        //will be called to update _wordPossibilities, and then we'll UpdateDisplay
                }
                else if (IsSpaceButton(senderBtn))
                {
                    //add the current word to the frozen history, then reset the current word
                    _frozenHistory += " " + _currentWordDisplay;
                    _currentInputs = _currentWordDisplay = String.Empty;
                    //_wordPossibilities.Clear();
                }
                else if (IsBackButton(senderBtn))
                {
                    //backspace if we can
                    if (_currentInputs.Length >= 1)
                    {
                        _currentInputs = _currentInputs.Substring(0, _currentInputs.Length - 1);
                        UpdateDisplay();
                    }//end if
                }//end which button?
                else if (IsNextButton(senderBtn))
                {
                    //cycle the currentWordPossibilities list if we can
                    var currentWordList = _wordPossibilities[_currentInputs];
                    if (currentWordList.Count > 1)
                    {
                        var first = currentWordList.First();
                        currentWordList.RemoveAt(0);
                        currentWordList.Add(first);
                        UpdateDisplay();
                    }//end if
                }//end if else

            }//end else T9 mode

        }//end T9Button_Click

        /// <summary>
        /// Toggle numeric/T9 states if we can, switches the label on the toggle button
        /// </summary>
        private void T9ToggleButton_Click(object sender, RoutedEventArgs e)
        {
            //toggle if we can
            if (_currentInputs == String.Empty)
            {
                _numericToggleOn = !_numericToggleOn;
                ((Button)sender).Content = _toggleLabels[_numericToggleOn];
            }//end if
        }//end T9ToggleButton_Click

        /// <summary>
        /// Sets up the labels for the 9 main buttons
        /// </summary>
        /// <param name="g">The common parent of the buttons</param>
        private void SetUpButtonContents(Grid g)
        {
            foreach (var child in g.Children)
            {
                if (child is Button)
                {
                    var btn = (Button)child;
                    btn.Content = ParseButtonTag(btn.Tag.ToString()) ?? btn.Content;
                }//end if
            }//end foreach
        }//end SetUpButtonContents

        /// <summary>
        /// Translates a XAML tag to a label
        /// </summary>
        /// <param name="btnTag">The Tag set in XAML</param>
        /// <returns>Returns the label string</returns>
        private String ParseButtonTag(String btnTag)
        {
            switch (btnTag.Substring(0, 1))
            {
                case "D":
                    char digit = Convert.ToChar(btnTag.Substring(1,1));
                    return digit.ToString() + System.Environment.NewLine + _Labels[digit];
                case "B":
                    return BACK_BUTTON.ToString() + System.Environment.NewLine + _Labels[BACK_BUTTON];
                case "N":
                    return NEXT_BUTTON.ToString() + System.Environment.NewLine + _Labels[NEXT_BUTTON];
                case "S":
                    return SPACE_BUTTON.ToString() + System.Environment.NewLine + _Labels[SPACE_BUTTON];
                case "X":
                    return null;
                default:
                    throw new ArgumentException("The Button Tag was invalid: " + btnTag);
            }//end switch
        }//end T9Button ParseButtonString(String)

        /// <summary>
        /// Trims the a Button's label to the identifying character
        /// </summary>
        /// <param name="btn">a Button</param>
        /// <returns>The character identifier</returns>
        private char GetButtonIdentifier(Button btn)
        {
            return Convert.ToChar(btn.Content.ToString().Trim().Substring(0, 1));
        }//end GetButtonIdentifier

        /// <summary>
        /// Checks to see if a Button is a digit input button
        /// </summary>
        /// <param name="btn">the Button</param>
        /// <param name="buttonDigit">output parameter set to the Button's identifier</param>
        /// <returns>Returns true if the button is a digit input button, otherwise false</returns>
        private bool IsDigitButton(Button btn, out char buttonDigit)
        {
            buttonDigit = GetButtonIdentifier(btn);
            if (DIGIT_BUTTONS_STRING.Split().Contains(buttonDigit.ToString()))
                return true;
            return false;
        }//end IsDigitButton

        /// <summary>
        /// Checks to see if a Button is the Back button
        /// </summary>
        private bool IsBackButton(Button btn)
        {
            return GetButtonIdentifier(btn) == BACK_BUTTON;
        }//end IsBackButton

        /// <summary>
        /// Checks to see if a button is the Space button
        /// </summary>
        private bool IsSpaceButton(Button btn)
        {
            return GetButtonIdentifier(btn) == SPACE_BUTTON;
        }//end IsSpaceButton

        /// <summary>
        /// Checks to see if a button is the Next button
        /// </summary>
        private bool IsNextButton(Button btn)
        {
            return GetButtonIdentifier(btn) == NEXT_BUTTON;
        }//end IsNextButton

        /// <summary>
        /// Checks to see if a button is the Quit button
        /// </summary>
        private bool IsQuitButton(Button btn)
        {
            return GetButtonIdentifier(btn) == QUIT_BUTTON;
        }

        /// <summary>
        /// Event handler that's called whenever the TextBox.Text changes
        /// Uses the ScrollViewer to autoscroll the textbox horizontally
        /// </summary>
        private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
            var textBox = (TextBox)sender;
            var scroller = (ScrollViewer)(textBox.Parent);
            scroller.ScrollToHorizontalOffset(scroller.ScrollableWidth);
        }//end IsQuitButton

    }//end class MainPage

}//end namespace T9Silverlight
